﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;

namespace ConsoleApplication1
{

    // Klasa algorytmu ECGA (projekt nr. 2)
    public class ECGA
    { 
        // Rozmiar populacji
        int N;
        // Rozmiar genotypu = liczba reguł
        int D;
        // Liczba iteracji 
        int iter;
        // Pojedynczy building block
        List<int> block;
        // Lista building blocków
        List<List<int>> S;
        // Bieżąca populacja algorytm
        public List<bool[]> curr_population;
        // Najlepszy ekspert
        public bool[] best_expert;
        // Tablica wartości f. celu
        public double[] zarobki;
        // Generator liczb pseudolosowych
        Random generator;

        // Konstruktor klasy algorytmu ECGA
        public ECGA(int N, int iter)
        {
            this.iter = iter;
            this.N = N;
            this.D = getAllIndex().Length;
        }

        // Inicjuje building blocki
        private void init_building_blocks()
        {
            S = new List<List<int>>();

            for (int i = 0; i < D; i++)
            {
                block = new List<int>();
                block.Add(i);
                S.Add(block);
            }
        }

        // Funkcja inicjująca losową populację 
        public void initPopulation()
        {
            curr_population = new List<bool[]>();
            bool[] curr_genotype = new bool[D];
            generator = new Random();

            for (int i = 0; i < N; i++)
            {
                curr_genotype = new bool[D];
                for (int j = 0; j < D; j++)
                {
                    if (generator.Next(2) == 0)
                    {
                        curr_genotype[j] = false;
                    }
                    else
                    {
                        curr_genotype[j] = true;
                    }
                }

                curr_population.Add(curr_genotype);
            }
        }


        // Ocena populacji -- f. sterująca, zwaraca odpowiadającą populacji listę wartości f. przystosowania 
        public double[] evalPopulation(List<bool[]> population, DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            Triple<double, double, double[]> t = objective_sum_min(population, dateFrom, dateTo, path, cash);
            double min = t.First;
            double sum = t.Second;
            double[] objective_vector = t.Third;

            return adaptation(min, sum, objective_vector);
        }

        // Funkcja przystosowania, zwraca wektor przystosowania
        public double[] adaptation(double min, double sum, double[] objective_vector)
        {
            double[] adaptation_vector = new double[objective_vector.Length];

            zarobki = objective_vector;

            for (int i = 0; i < objective_vector.Length; i++)
            {
                adaptation_vector[i] = (double)((objective_vector[i] - min) / (sum - objective_vector.Length * min));
            }

            return adaptation_vector;
        }

        public Triple<double, double, double[]> objective_sum_min(List<bool[]> population, DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            // suma wszystkich wartości f. celu
            double sum = 0;
            // min (najgorsza wart. f. celu)
            double min = double.MaxValue;
            // tablica kolejnych wart. f. celu dla kolejnych osbników
            double[] objective = new double[population.Count];

            Index[] indexes = getAllIndex();
            Index m;
            List<Index.Recomendation> recomendations;

            List<Record> list = Data.readFromFile(path, dateFrom, dateTo);
            int days = list.Count;

            // tablica będzie trzymać decyzję i-tej reguły, danego dnia (-1,0,1)
            Index.Recomendation[,] table = new Index.Recomendation[D, days];

            // uzupełnianie powyzszej tablicy
            for (int j = 0; j < D; j++)
            {
                m = indexes[j];

                for (int k = 0; k < days; k++)
                {
                    Index.Recomendation rec = m.eval(list, k + 1);

                    table[j, k] = rec;
                }
            }

            // sprawdzanie co robią kolejni osobnicy
            for (int i = 0; i < population.Count; i++)
            {
                recomendations = new List<Index.Recomendation>();

                // dla kolejnych dni
                for (int k = 0; k < days; k++)
                {
                    int decisionNone = 0;
                    int decisionBuy = 0;
                    int decisionSell = 0;

                    for (int j = 0; j < D; j++)
                    {
                        if (population[i][j])
                        {
                            if (table[j, k] == Index.Recomendation.buy)
                                decisionBuy++;
                            else if (table[j, k] == Index.Recomendation.sell)
                                decisionSell++;
                            else
                                decisionNone++;
                        }
                    }
                    int x = decisionBuy + decisionNone + decisionSell;
                    if (decisionSell > decisionNone && decisionSell > decisionBuy)
                        recomendations.Add(Index.Recomendation.sell);
                    else if (decisionBuy > decisionNone && decisionBuy > decisionSell)
                        recomendations.Add(Index.Recomendation.buy);
                    else
                        recomendations.Add(Index.Recomendation.none);
                }

                // zarobek i-tego osobnika
                objective[i] = (double)countEarnings(list, recomendations, (int)(cash / list[0].Close), cash);

                if (objective[i] < min)
                    min = objective[i];
                int xy = (int)(cash / list[0].Close);

                sum += objective[i];
            }

            return new Triple<double, double, double[]>(min, sum, objective);
        }

        // Wyświetla populację
        public void show_pop()
        {
            for (int i = 0; i < N; i++)
            {
                for (int j = 0; j < D; j++)
                {
                    if (curr_population[i][j])
                        Console.Write(" 1");
                    else
                        Console.Write(" 0");
                }
                Console.WriteLine();
            }
        }

        // Wyświetla zarobki
        public void show_zarobki()
        {
            for (int i = 0; i < N; i++)
            {
                Console.WriteLine(zarobki[i].ToString());
            }
        }

        // Generuje losową permutację ro dla metody population_generating
        public int[] ro_generating()
        {
            int[] seed = new int[N];

            int i, j, k, pos, zakres = N;

            for (i = 0; i < N; i++)
                seed[i] = -1;

            for (i = 0; i < N; i++)
            {
                pos = generator.Next(zakres);

                if (seed[pos] == -1)
                    seed[pos] = i;
                else
                {
                    k = 0;

                    for (j = 0; j < N; j++)
                    {
                        if (seed[j] == -1)
                        {
                            if (k == pos)
                                seed[j] = i;
                            k++;
                        }
                    }
                }

                zakres--;
            }

            return seed;
        }


        // Generuje nową populację na podstawie building blocków
        public List<bool[]> population_generating()
        {
            Console.WriteLine("Generuję populację");

            int[] ro = new int[N];
            List<bool[]> new_population = new List<bool[]>(curr_population);

            for (int i = 0; i < S.Count; i++)
            {
                ro = ro_generating(); 
                for (int k = 0; k < N; k++)
                {
                    for (int j = 0; j < S[i].Count; j++)
                    {
                        new_population[k][S[i][j]] = curr_population[ro[k]][S[i][j]];
                    }
                }
               
            }

            Console.WriteLine("Wygenerowałem populację");

            return new_population;
        }

        public List<int> blockSum(List<int> a, List<int> b)
        {
            List<int> result = new List<int>(a);

            foreach (int x in b)
            {
                result.Add(x);
            }
            result.Sort();
            return result;
        }

        public List<bool> intToBinary(int q, int c)
        {
            List<bool> result = new List<bool>();

             for (int i = 0; i < c; i++)
             {
                  if (q % 2 == 1)
                     result.Add(true);
                  else
                     result.Add(false);

                  q /= 2;
             }

             result.Reverse();
             return result;
        }

        public int countPattern(List<bool> b, List<int> index)
        {

            int result = 0;
            foreach (bool[] s in curr_population)
            {

                bool correct = true;
                for (int i = 0; i < b.Count; i++)
                {
                    if (s[index[i]] != b[i])
                    {
                        correct = false;
                        break;
                    }
                }

                if (correct)
                {
                    result++;
                }
            }

            return result;
        }

        /*public double combinedComplexity(List<List<int>> s, List<bool[]> p )
        {
            double mc = 0.0;
            foreach (List<int> b in s)
            {
                mc += Math.Pow(2.0, b.Count);
            }
            mc *= Math.Log(N, 2.0);

            double cpc = 0.0;

            foreach (List<int> b in s)
            {
                int qMax = (int)(Math.Pow(2.0, b.Count));
                for (int q = 0; q < qMax; q++)
                {                    
                    List<bool> bq = intToBinary(q, b.Count);
                    int param = countPattern(bq, b);

                    if (param != 0)
                    {
                        double temp2 = (double)param / N;
                        cpc -= temp2 * Math.Log(temp2, 2.0);
                    }
                }
            }

            return mc + cpc;
        }

        public void chromosomePartitionUpdating()
        {
            // curr_population
            // S     

            
            List<List<int>> temp;

            Console.WriteLine("Dzielę chromosom");
            while (true)
            {
                List<List<int>> sMin = new List<List<int>>();
                double ccMin = combinedComplexity(S, curr_population);
                for (int i = 0; i < S.Count; i++) 
                {
                    for (int j = i+1; j < S.Count; j++) 
                    {
                         temp = new List<List<int>>(S);
                         temp.RemoveAt(j);
                         temp.RemoveAt(i);

                         temp.Add(blockSum(S[i], S[j]));

                         double cc = combinedComplexity(temp, curr_population);
                         Console.WriteLine(ccMin.ToString() + " " + cc.ToString());
                         if (cc < ccMin)
                         {
                             sMin = new List<List<int>>(temp);
                             ccMin = cc;
                         }

                        // cctemp                        
                    }
                }
                Console.WriteLine("Podzieliłem chromosom");
                if (sMin.Count > 0)
                {
                    S = sMin;
                }
                if (sMin.Count == 0)
                {
                    break;
                }
            }
        }*/

        public double licz_cc(List<int> b)
        {
            double mc = 0.0;
            
            mc = Math.Pow(2.0, b.Count);

            mc *= Math.Log(N, 2.0);

            double cpc = 0.0;

            int qMax = (int)(Math.Pow(2.0, b.Count));
            for (int q = 0; q < qMax; q++)
            {
                List<bool> bq = intToBinary(q, b.Count);
                int param = countPattern(bq, b);

                if (param != 0)
                {
                    double temp2 = (double)param / N;
                    cpc -= temp2 * Math.Log(temp2, 2.0);
                }
            }

            return mc + cpc;

        }

        public void chromosomePartitionUpdating()
        {
            int bi = -1; 
            int bj = -1; 
            double bcomp = 1.0;

            Console.WriteLine("Dzielę chromosom");

            while (bcomp > 0.0)
            {
                bcomp = -1.0;
                for (int i = 0; i < S.Count; i++)
                {
                    for (int j = i + 1; j < S.Count; j++)
                    {
                        double ci = licz_cc(S[i]);
                        double cj = licz_cc(S[j]);
                        double cij = licz_cc(blockSum(S[i], S[j]));
                        double deltaij = ci + cj - cij;

                        if (deltaij >= bcomp)
                        {
                            bi = i;
                            bj = j;
                            bcomp = deltaij;
                        }
                    }
                }

                if (bcomp != -1)
                {
                    Console.WriteLine("Podzieliłem chromosom");
                    List<int> temp = new List<int>(blockSum(S[bi], S[bj]));
                    S.RemoveAt(bj);
                    S.RemoveAt(bi);
                    S.Add(temp);
                }


            }

        }
        // Zapisuje partycję końcową do pliku
        private void save_partition()
        {
            TextWriter tw = new StreamWriter("partycja.txt");

            foreach (List<int> b in S)
            {
                foreach (int j in b)
                {
                    tw.Write(" " + j.ToString());
                }

                tw.WriteLine();
            }

            tw.Close();
        }

        // Metoda sterująca algorytmem
        public void doTheEvolution(DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            init_building_blocks();
            initPopulation();
            double[] adaptation = evalPopulation(curr_population, dateFrom, dateTo, path, cash);
            double max = double.MinValue;
            double inner_max = double.MinValue;
            TextWriter tw = new StreamWriter("dane.txt");


            // kolejne iteracje algorytmu
            for (int i = 0; i < iter; i++)
            {
                chromosomePartitionUpdating();
                curr_population = population_generating();
                adaptation = evalPopulation(curr_population, dateFrom, dateTo, path, cash);

                inner_max = double.MinValue;
                for (int x = 0; x < N; x++)
                {
                    if (max < zarobki[x])
                    {
                        max = zarobki[x];
                        best_expert = new bool[D];
                        best_expert = curr_population[x];
                    }

                    if (inner_max < zarobki[x])
                    {
                        inner_max = zarobki[x];
                    }

                }
                Console.WriteLine(i.ToString() + ". " + inner_max.ToString());
                tw.WriteLine(i.ToString() + " " + inner_max.ToString());
                save_partition();
            }
            tw.Close();
            save_partition();
        }

        // Symuluje najlepszego znalezionego eksperta
        public double simulate_best_expert(DateTime dateFrom, DateTime dateTo, string path, decimal cash)
        {
            Index[] indexes = getAllIndex();
            Index m;
            List<Index.Recomendation> recomendations;

            List<Record> list = Data.readFromFile(path, dateFrom, dateTo);
            int days = list.Count;

            // tablica będzie trzymać decyzję i-tej reguły, danego dnia (-1,0,1)
            Index.Recomendation[,] table = new Index.Recomendation[D, days];

            // uzupełnianie powyzszej tablicy
            for (int j = 0; j < D; j++)
            {
                m = indexes[j];

                for (int k = 0; k < days; k++)
                {
                    Index.Recomendation rec = m.eval(list, k + 1);

                    table[j, k] = rec;
                }
            }

            recomendations = new List<Index.Recomendation>();

            // dla kolejnych dni
            for (int k = 0; k < days; k++)
            {
                int decisionNone = 0;
                int decisionBuy = 0;
                int decisionSell = 0;

                for (int j = 0; j < D; j++)
                {
                    if (best_expert[j])
                    {
                        if (table[j, k] == Index.Recomendation.buy)
                            decisionBuy++;
                        else if (table[j, k] == Index.Recomendation.sell)
                            decisionSell++;
                        else
                            decisionNone++;
                    }
                }

                if (decisionSell > decisionNone && decisionSell > decisionBuy)
                    recomendations.Add(Index.Recomendation.sell);
                else if (decisionBuy > decisionNone && decisionBuy > decisionSell)
                    recomendations.Add(Index.Recomendation.buy);
                else
                    recomendations.Add(Index.Recomendation.none);
            }

            // zarobek maksymalnego eksperta
            return (double)countEarnings(list, recomendations, (int)(cash / list[0].Close), cash);

        }

        /*
        * Liczy zysk jaki osiągneliśmy
        * Uwaga - pierwszy dzien jest pomijany w obliczeniach. Jest to baza do obliczenia poczatkowej wartosci portfela.
        * 
        * data - ceny akcji (musi miec przynajmniej jedna pozycje).
        * recomendations - rekomendacje (dlugosc dat i recomendation musi byc taka sama)
        * stocks - poczatkowa liczba akcji
        * money - poczatkowa kwota 
        * 
        * Zwraca ostateczna kwote jaka udalo sie zarobic (wartosc koncowa akcji i gotowki - wartosc poczatkowa akcji i gotowki)
        */

        public decimal countEarnings(List<Record> data, List<Index.Recomendation> recomendations, int stocks, decimal money)
        {
            //Console.WriteLine(data.Count.ToString());
            if (data.Count != recomendations.Count)
                throw new Exception("Tablice danych i rekomentacji musz miec ten sam rozmiar");

            int count = data.Count;

            decimal valueStart = money + stocks * data[0].Close;

            for (int i = 1; i < count; i++)
            {
                if (recomendations[i] == Index.Recomendation.buy)
                {
                    int toBuy = stocks;
                    decimal cost = toBuy * data[i].Close;
                    if (cost > money)
                    {
                        toBuy = (int)(money / data[i].Close);
                        cost = toBuy * data[i].Close;
                    }

                    stocks += toBuy;
                    money -= cost;
                }
                else if (recomendations[i] == Index.Recomendation.sell)
                {
                    int toSell = stocks / 2;
                    money += toSell * data[i].Close;
                    stocks -= toSell;
                }
            }

            decimal valueEnd = money + stocks * data[count - 1].Close;

            return valueEnd - valueStart;
        }

        // Wskaźniki
        public Index[] getAllIndex()
        {
            return new Index[] {
            new MovingAverage(5),
            new MovingAverage(6),
            new MovingAverage(7),
            new MovingAverage(8),
            new MovingAverage(9),
            new MovingAverage(10),
            new MovingAverage(11),
            new MovingAverage(12),
            new MovingAverage(13),
            new MovingAverage(14),
            new MovingAverage(15),
            new MovingAverage(16),
            new MovingAverage(17),
            new MovingAverage(18),
            new MovingAverage(19),
            new MovingAverage(20),
            new MovingAverage(21),
            new MovingAverage(22),
            new MovingAverage(23),
            new MovingAverage(24),
            new MovingAverage(25),
            new MovingAverage(26),
            new MovingAverage(27),
            new MovingAverage(28),
            new MovingAverage(29),
            new MovingAverage(30),
            new MovingAverage(31),
            new MovingAverage(32),/*
            
            new Macd (20, 12, 9),
            new Macd (26, 12, 9),            
            new Macd (30, 12, 9),
            new Macd (40, 12, 9),
            new Macd (20, 8, 9),
            new Macd (26, 8, 9),
            new Macd (30, 8, 9),
            new Macd (40, 8, 9),
            new Macd (20, 15, 9),
            new Macd (26, 15, 9),
            new Macd (30, 15, 9),
            new Macd (40, 15, 9),
            new Macd (20, 12, 5),
            new Macd (26, 12, 5),
            new Macd (30, 12, 5),
            new Macd (40, 12, 5),
            new Macd (20, 15, 13),
            new Macd (26, 15, 13),
            new Macd (30, 15, 13),
            new Macd (40, 15, 13),
            new Macd (50, 25, 23),
            new Macd (45, 40, 33),
            new Macd (12, 10, 9),
            new Macd (6, 4, 2),
            new Macd (8, 7, 6),
            new Macd (14, 12, 9),
            new Macd (7, 4, 2),
            new Macd (9, 7, 6),
                                   

            new Rsi(5),
            new Rsi(6),
            new Rsi(7),
            new Rsi(8),
            new Rsi(9),
            new Rsi(10),
            new Rsi(11),
            new Rsi(12),
            new Rsi(13),
            new Rsi(14),
            new Rsi(15),
            new Rsi(16),
            new Rsi(17),
            new Rsi(18),
            new Rsi(19),
            new Rsi(20),
            new Rsi(21),
            new Rsi(22),
            new Rsi(23),
            new Rsi(24),
            new Rsi(25),
            new Rsi(26),
            new Rsi(27),
            new Rsi(28),
            new Rsi(29),
            new Rsi(30),
            new Rsi(31),
            new Rsi(32),

            new Ema(5),
            new Ema(6),
            new Ema(7),
            new Ema(8),
            new Ema(9),
            new Ema(10),
            new Ema(11),
            new Ema(12),
            new Ema(13),
            new Ema(14),
            new Ema(15),
            new Ema(16),
            new Ema(17),
            new Ema(18),
            new Ema(19),
            new Ema(20),
            new Ema(21),
            new Ema(22),
            new Ema(23),
            new Ema(24),
            new Ema(25),
            new Ema(26),
            new Ema(27),
            new Ema(28),
            new Ema(29),
            new Ema(30),
            new Ema(31),
            new Ema(32),

            new Wma(5),
            new Wma(6),
            new Wma(7),
            new Wma(8),
            new Wma(9),
            new Wma(10),
            new Wma(11),
            new Wma(12),
            new Wma(13),
            new Wma(14),
            new Wma(15),
            new Wma(16),
            new Wma(17),
            new Wma(18),
            new Wma(19),
            new Wma(20),
            new Wma(21),
            new Wma(22),
            new Wma(23),
            new Wma(24),
            new Wma(25),
            new Wma(26),
            new Wma(27),
            new Wma(28),
            new Wma(29),
            new Wma(30),
            new Wma(31),
            new Wma(32),*/
            

            new EmaMa(5, 20),
            new EmaMa(6, 19),
            new EmaMa(7, 18),
            new EmaMa(8, 17),
            new EmaMa(9, 16),
            new EmaMa(10, 15),
            new EmaMa(11, 14),
            new EmaMa(12, 13),
            new EmaMa(13, 12),
            new EmaMa(14, 11),
            new EmaMa(15, 10),
            new EmaMa(16, 9),
            new EmaMa(17, 8),
            new EmaMa(18, 7),
            new EmaMa(19, 6),
            new EmaMa(20, 5),
            new EmaMa(10, 10),
            new EmaMa(25, 15),
            new EmaMa(35, 25),
            new EmaMa(25, 35),
            new EmaMa(9, 9),
            new EmaMa(5, 5),
            new EmaMa(11, 5),
            new EmaMa(22, 18),
            new EmaMa(18, 14),
            new EmaMa(31, 21),
            new EmaMa(41, 21),

            new WmaMa(5, 20),
            new WmaMa(6, 19),
            new WmaMa(7, 18),
            new WmaMa(8, 17),
            new WmaMa(9, 16),
            new WmaMa(10, 15),
            new WmaMa(11, 14),
            new WmaMa(12, 13),
            new WmaMa(13, 12),
            new WmaMa(14, 11),
            new WmaMa(15, 10),
            new WmaMa(16, 9),
            new WmaMa(17, 8),
            new WmaMa(18, 7),
            new WmaMa(19, 6),
            new WmaMa(20, 5),
            new WmaMa(10, 10),
            new WmaMa(25, 15),
            new WmaMa(35, 25),
            new WmaMa(25, 35),
            new WmaMa(9, 9),
            new WmaMa(5, 5),
            new WmaMa(11, 5),
            new WmaMa(22, 18),
            new WmaMa(18, 14),
            new WmaMa(31, 21),
            new WmaMa(41, 21)/*,
         
            new RWilliams(5),
            new RWilliams(6),
            new RWilliams(7),
            new RWilliams(8),
            new RWilliams(9),
            new RWilliams(10),
            new RWilliams(11),
            new RWilliams(12),
            new RWilliams(13),
            new RWilliams(14),
            new RWilliams(15),
            new RWilliams(16),
            new RWilliams(17),
            new RWilliams(18),
            new RWilliams(19),
            new RWilliams(20),
            new RWilliams(21),
            new RWilliams(22),
            new RWilliams(23),
            new RWilliams(24),
            new RWilliams(25),
            new RWilliams(26),
            new RWilliams(27),
            new RWilliams(28),
            new RWilliams(29),
            new RWilliams(30),
            new RWilliams(31),
            new RWilliams(32),
            
            new MoneyFlow(5),
            new MoneyFlow(6),
            new MoneyFlow(7),
            new MoneyFlow(8),
            new MoneyFlow(9),
            new MoneyFlow(10),
            new MoneyFlow(11),
            new MoneyFlow(12),
            new MoneyFlow(13),
            new MoneyFlow(14),
            new MoneyFlow(15),
            new MoneyFlow(16),
            new MoneyFlow(17),
            new MoneyFlow(18),
            new MoneyFlow(19),
            new MoneyFlow(20),
            new MoneyFlow(21),
            new MoneyFlow(22),
            new MoneyFlow(23),
            new MoneyFlow(24),
            new MoneyFlow(25),
            new MoneyFlow(26),
            new MoneyFlow(27),
            new MoneyFlow(28),
            new MoneyFlow(29),
            new MoneyFlow(30),
            new MoneyFlow(31),
            new MoneyFlow(32)*/
            

           
        };
        }

    }
}
